package ling.learn.syntax;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

public class SyntaxGraph {

  private Map<String, Map<String, SyntaxEdge>> adjList;
  private String end;

  public SyntaxGraph() {
    this.adjList = new ConcurrentHashMap<>();
  }

  public void addEdge(String from, String to, String content) {
    adjList.computeIfAbsent(from, k -> new ConcurrentHashMap<>())
        .computeIfAbsent(to, k -> new SyntaxEdge(from, to)).addContent(content);

    if (to.endsWith("END")) {
      end = to;
    }
  }

  public List<List<String>> findAllPaths(String start) {
    List<List<String>> paths = new ArrayList<>();
    List<String> path = new ArrayList<>();
    Set<String> visited = new HashSet<>();
    String visitedEdge = String.join("->", start, end);
    visited.add(visitedEdge);
    dfs(start, end, visited, path, paths);
    visited.remove(visitedEdge);
    return paths;
  }

  public List<List<String>> findAllPaths(String start, String end) {
    List<List<String>> paths = new ArrayList<>();
    List<String> path = new ArrayList<>();
    Set<String> visited = new HashSet<>();
    String visitedEdge = String.join("->", start, end);
    visited.add(visitedEdge);
    dfs(start, end, visited, path, paths);
    visited.remove(visitedEdge);
    return paths;
  }

  private void dfs(String current, String end, Set<String> visited, List<String> path,
      List<List<String>> paths) {
    path.add(current);
    if (current.equals(end)) {
      paths.add(new ArrayList<>(path));
    } else {
      Map<String, SyntaxEdge> neighbors =
          this.adjList.getOrDefault(current, new ConcurrentHashMap<>());
      for (String neighbor : neighbors.keySet()) {
        String visitedEdge = String.join("->", current, neighbor);
        if (!visited.contains(visitedEdge)) {
          visited.add(visitedEdge);
          dfs(neighbor, end, visited, path, paths);
          visited.remove(visitedEdge);
        }
      }
    }
    path.remove(path.size() - 1);
  }

  public String formatPath(List<String> path) {
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < path.size(); i++) {
      // sb.append(path.get(i));
      if (i < path.size() - 1) {
        SyntaxEdge edge = getEdge(path.get(i), path.get(i + 1));
        if (edge != null) {
          sb.append(edge.toString());
        }
        // sb.append("->");
        sb.append(" ");
      }
    }
    return sb.toString();
  }

  private SyntaxEdge getEdge(String from, String to) {
    Map<String, SyntaxEdge> edges = this.adjList.get(from);
    if (edges != null) {
      return edges.get(to);
    }
    return null;
  }
}
